package cloud.seri.auth.service

import cloud.seri.auth.SeriAuthApp
import cloud.seri.auth.config.Constants
import cloud.seri.auth.domain.User
import cloud.seri.auth.repository.UserRepository
import cloud.seri.auth.service.util.RandomUtil

import org.apache.commons.lang3.RandomStringUtils
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.domain.PageRequest
import org.springframework.test.context.junit4.SpringRunner

import java.time.Instant
import java.time.temporal.ChronoUnit

import org.assertj.core.api.Assertions.assertThat
import kotlin.test.assertNotNull

/**
 * Integration tests for [UserService].
 */
@RunWith(SpringRunner::class)
@SpringBootTest(classes = [SeriAuthApp::class])
class UserServiceIT {

    @Autowired
    private lateinit var userRepository: UserRepository

    @Autowired
    private lateinit var userService: UserService

    private lateinit var user: User

    @Before
    fun init() {
        userRepository.deleteAll()
        user = User()
        user.login = "johndoe"
        user.password = RandomStringUtils.random(60)
        user.activated = true
        user.email = "johndoe@localhost"
        user.firstName = "john"
        user.lastName = "doe"
        user.imageUrl = "http://placehold.it/50x50"
        user.langKey = "en"
    }

    @Test
    fun assertThatUserMustExistToResetPassword() {
        userRepository.save(user)
        var maybeUser = userService.requestPasswordReset("invalid.login@localhost")
        assertThat(maybeUser).isNotPresent

        maybeUser = userService.requestPasswordReset(user.email!!)
        assertThat(maybeUser).isPresent
        assertThat(maybeUser.orElse(null).email).isEqualTo(user.email)
        assertThat(maybeUser.orElse(null).resetDate).isNotNull()
        assertThat(maybeUser.orElse(null).resetKey).isNotNull()
    }

    @Test
    fun assertThatOnlyActivatedUserCanRequestPasswordReset() {
        user.activated = false
        userRepository.save(user)

        val maybeUser = userService.requestPasswordReset(user.login!!)
        assertThat(maybeUser).isNotPresent
        userRepository.delete(user)
    }

    @Test
    fun assertThatResetKeyMustNotBeOlderThan24Hours() {
        val daysAgo = Instant.now().minus(25, ChronoUnit.HOURS)
        val resetKey = RandomUtil.generateResetKey()
        user.activated = true
        user.resetDate = daysAgo
        user.resetKey = resetKey
        userRepository.save(user)

        val maybeUser = userService.completePasswordReset("johndoe2", user.resetKey!!)
        assertThat(maybeUser).isNotPresent
        userRepository.delete(user)
    }

    @Test
    fun assertThatResetKeyMustBeValid() {
        val daysAgo = Instant.now().minus(25, ChronoUnit.HOURS)
        user.activated = true
        user.resetDate = daysAgo
        user.resetKey = "1234"
        userRepository.save(user)

        val maybeUser = userService.completePasswordReset("johndoe2", user.resetKey!!)
        assertThat(maybeUser).isNotPresent
        userRepository.delete(user)
    }

    @Test
    fun assertThatUserCanResetPassword() {
        val oldPassword = user.password
        val daysAgo = Instant.now().minus(2, ChronoUnit.HOURS)
        val resetKey = RandomUtil.generateResetKey()
        user.activated = true
        user.resetDate = daysAgo
        user.resetKey = resetKey
        userRepository.save(user)

        val maybeUser = userService.completePasswordReset("johndoe2", user.resetKey!!)
        assertThat(maybeUser).isPresent
        assertThat(maybeUser.orElse(null).resetDate).isNull()
        assertThat(maybeUser.orElse(null).resetKey).isNull()
        assertThat(maybeUser.orElse(null).password).isNotEqualTo(oldPassword)

        userRepository.delete(user)
    }

    @Test
    fun testFindNotActivatedUsersByCreationDateBefore() {
        val now = Instant.now()
        user.activated = false
        val dbUser = userRepository.save(user)
        assertNotNull(dbUser)
        dbUser.createdDate = now.minus(4, ChronoUnit.DAYS)
        userRepository.save(user)
        var users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minus(3, ChronoUnit.DAYS))
        assertThat(users).isNotEmpty
        userService.removeNotActivatedUsers()
        users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(now.minus(3, ChronoUnit.DAYS))
        assertThat(users).isEmpty()
    }

    @Test
    fun assertThatAnonymousUserIsNotGet() {
        user.login = Constants.ANONYMOUS_USER
        if (!userRepository.findOneByLogin(Constants.ANONYMOUS_USER).isPresent) {
            userRepository.save(user)
        }
        val pageable = PageRequest.of(0, userRepository.count().toInt())
        val allManagedUsers = userService.getAllManagedUsers(pageable)
        assertNotNull(allManagedUsers)
        assertThat(allManagedUsers.content.stream()
            .noneMatch { user -> Constants.ANONYMOUS_USER == user.login })
            .isTrue()
    }

    @Test
    fun testRemoveNotActivatedUsers() {

        user.activated = false
        userRepository.save(user)
        // Let the audit first set the creation date but then update it
        user.createdDate = Instant.now().minus(30, ChronoUnit.DAYS)
        userRepository.save(user)

        assertThat(userRepository.findOneByLogin("johndoe")).isPresent
        userService.removeNotActivatedUsers()
        assertThat(userRepository.findOneByLogin("johndoe")).isNotPresent
    }
}
